package fr.jgraph.model.path;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import fr.jgraph.model.edge.old.Edge;
import fr.jgraph.model.vertex.old.Vertex;

/** Unoriented */
public class Chain {
	private List vertices;
	private List edges;
	
	private List getVertices() {
		return vertices;
	}
	
	private List getEdges() {
		return edges;
	}
	
	/** Inits the chain with the departure point and the edge given in parameter */
	public Chain(Vertex vertex, List edges) {
		vertices = new ArrayList();
		edges = new ArrayList();
		
		vertices.add(vertex);
		
		Iterator itEdges = edges.iterator();
		while(itEdges.hasNext()) {
			add((Edge) itEdges.next());
		}
	}
	
	protected Edge getLastEdge() {
		return (Edge) edges.get(edges.size()-1);
	}
	
	protected Vertex getLastVertex() {
		return (Vertex) vertices.get(vertices.size()-1);
	}
	
	protected Vertex getFirstVertex() {
		return (Vertex) vertices.get(0);
	}
	
	/** Adds an edge at the beginning of the chain */
	public void addFirst(Edge edge) {
		Vertex first = getFirstVertex();
		
		Vertex[] ends = edge.getEnds();
		
		if(ends[0].equals(first)) {
			vertices.add(0,ends[1]);
			edges.add(0,edge);
		} else if(ends[1].equals(first)) {
			vertices.add(0,ends[0]);
			edges.add(0,edge);
		} else {
			throw new IllegalArgumentException("The edge cannot be added here.");
		}
	}
	
	/** Adds an edge at the end of the chain */
	public void add(Edge edge) {
		Vertex last = getLastVertex();
		
		Vertex[] ends = edge.getEnds();
		
		if(ends[0].equals(last)) {
			vertices.add(ends[1]);
			edges.add(edge);
		} else if(ends[1].equals(last)) {
			vertices.add(ends[0]);
			edges.add(edge);
		} else {
			throw new IllegalArgumentException("The edge cannot be added here.");
		}
	}
	
	public Edge removeLastEdge() {
		Edge last = getLastEdge();
		edges.remove(last);
		return last;
	}
	
	public boolean isEmpty() {
		return edges.isEmpty();
	}
	
	public Vertex[] getExtremities() {
		return new Vertex[] { getFirstVertex(), getLastVertex() };
	}
	
	public int getLength() {
		return edges.size();
	}
	
	public boolean equals(Object obj) {
		if(obj instanceof Chain) {
			Chain chain = (Chain) obj;
			List chEdges = chain.getEdges();
			List chVertices = chain.getVertices();
			if (edges.equals(chEdges) && vertices.equals(chVertices))
				return true;
			
			List invEdges = new ArrayList();
			List invVertices = new ArrayList();
			
			for(int i=chEdges.size()-1;i>=0;i--)
				invEdges.add(edges.get(i));
			for(int i=chVertices.size()-1;i>=0;i--)
				invVertices.add(vertices.get(i));
			
			if (edges.equals(invEdges) && vertices.equals(invVertices))
				return true;
		}
		return false;
	}
	
	/** A chain is elementary iif each vertex appears once or less. */
	public boolean isElementary() {
		return new HashSet(vertices).size() == vertices.size();
	}
	
	/** A chain is simple iif each edge appears once or less. */
	public boolean isSimple() {
		return new HashSet(edges).size() == edges.size();
	}
	
	/** A chain is closed iif v0=vk */
	public boolean isClosed() {
		return getFirstVertex().equals(getLastVertex());
	}
	
	/** A cycle is a simple and closed chain */
	public boolean isCycle() {
		return isClosed() && isSimple();
	}
}
